package com.android.physics;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import android.graphics.Canvas;

import com.android.physics.Time.OnTimeListener;

public class Stage extends PhysicsObject
{
    private final Time mTime;

    private final PhysicsObjectInStage<Actor> mActorInStage;
    
    private int mActorStartPosX;
    private int mActorStartPosY;
    private float mGravity;
    private float mStageSpeed;
    private float mStageMaxSpeed;
    
    private List<PhysicsObjectInStage<Roadblock>> mRoadblocksInStage = 
        new ArrayList<PhysicsObjectInStage<Roadblock>>();
    
    private RoadblockGenerator mRoadblockGenerator;
    
    public Stage(Actor actor, Time time)
    {
        mActorInStage = new PhysicsObjectInStage<Actor>(actor);

        mTime = time;
        mTime.regOnTimeListener(mOnTimeListener);
        
        mRoadblockGenerator = new RoadblockGenerator();
    }
    
    public void setActorStartPos(int x, int y)
    {
        mActorStartPosX = x;
        mActorStartPosY = y;
    }
    
    public void setGravity(float gravity)
    {
        mGravity = gravity;
    }
    
    public void setStageSpeed(float speed)
    {
        mStageSpeed = speed;  
    }
    
    public void setStageMaxSpeed(float speed)
    {
        mStageMaxSpeed = speed;
    }
    
    public void reset()
    {
        mActorInStage.setPos(mActorStartPosX, mActorStartPosY);
        mActorInStage.setMaxSpeed(mStageMaxSpeed, mStageMaxSpeed);
        mTime.reset();
        mRoadblockGenerator.resetTimeStamp();
        mRoadblocksInStage.clear();
        tryToLoadNewRoadblock();
    }
    
    public void release()
    {
        mTime.unregOnTimeListener(mOnTimeListener);
    }

    public void draw(Canvas canvas)
    {
        //TODO: draw stage
        
        // draw roadblocks
        for (PhysicsObjectInStage<Roadblock> roadblockInStage : mRoadblocksInStage)
        {
            roadblockInStage.drawInStage(canvas);
        }
        
        // draw actor
        mActorInStage.drawInStage(canvas);
    }

    private OnTimeListener mOnTimeListener = new OnTimeListener() 
    {
        @Override
        public void onTick()
        {
            //move blocks
            Iterator<PhysicsObjectInStage<Roadblock>> iterator = 
                mRoadblocksInStage.iterator();
            while (iterator.hasNext())
            {
                PhysicsObjectInStage<Roadblock> roadblockInStage = iterator.next();
                roadblockInStage.move();
                if (roadblockInStage.getEndTick() < mTime.getCurrentTick())
                {
                    iterator.remove();
                }
            }
            Actor actor = mActorInStage.getPhysicsObject();
            mActorInStage.addSpeedY(actor.getMoveStepY() + mGravity);
            mActorInStage.move();
            
            if ( actorHitTest() || limitInStage(mActorInStage) )
            {
                mActorInStage.getPhysicsObject().hit();
            }
            tryToLoadNewRoadblock();
            notifyOnStageUpdate();
        }
    };
    
    private boolean actorHitTest()
    {
        boolean isHit = false;
        for (PhysicsObjectInStage<Roadblock> roadblockInStage : mRoadblocksInStage)
        {
            float aLeft = roadblockInStage.getPosX();
            float aRight = roadblockInStage.getPosX() + roadblockInStage.getWidth();
            float aTop = roadblockInStage.getPosY();
            float aBottom = roadblockInStage.getPosY() + roadblockInStage.getHeight();
            
            float bLeft = mActorInStage.getPosX();
            float bRight = mActorInStage.getPosX() + mActorInStage.getWidth();
            float bTop = mActorInStage.getPosY();
            float bBottom = mActorInStage.getPosY() + mActorInStage.getHeight();
            
            isHit = aLeft < bRight && bLeft < aRight
                && aTop < bBottom && bTop < aBottom;
            if (isHit)
                break;
        }
        
        return isHit;
    }
    
    @SuppressWarnings("rawtypes")
    private boolean limitInStage(PhysicsObjectInStage actorInStage)
    {
        int newPosX = (int)actorInStage.getPosX();
        int newPosY = (int)actorInStage.getPosY();
        int maxWidth = getWidth() - actorInStage.getWidth();
        int maxHeight = getHeight() - actorInStage.getHeight();
        boolean isHit = false;
        if (newPosX < 0)
        {
            newPosX = 0;
            isHit = true;
        }
        else if (newPosX > maxWidth)
        {
            newPosX = maxWidth;
            isHit = true;
        }

        if (newPosY < 0)
        {
            newPosY = 0;
            isHit = true;
        }
        else if (newPosY > maxHeight)
        {
            newPosY = maxHeight;
            isHit = true;
        }
        
        actorInStage.setPos(newPosX, newPosY);
        return isHit;
    }
    
    private void tryToLoadNewRoadblock()
    {
        while (mTime.getCurrentTick() >= mRoadblockGenerator.getNextObjectTimeStamp())
        {
            mRoadblocksInStage.add(mRoadblockGenerator.create());
        }
    }
    
    public interface OnStageListener
    {
        public void onUpdate();
    }
    
    private List<OnStageListener> mOnStageListeners = 
        new ArrayList<Stage.OnStageListener>();
    
    public void regOnStageListener(OnStageListener listener)
    {
        if (listener == null)
        {
            throw new IllegalArgumentException("Input value is null");
        }
        mOnStageListeners.add(listener);
    }
    
    public void unregOnStageListener(OnStageListener listener)
    {
        mOnStageListeners.remove(listener);
    }
    
    private void notifyOnStageUpdate()
    {
        for (OnStageListener listener : mOnStageListeners)
        {
            listener.onUpdate();
        }
    }
}
